package org.infodavid.common.ejb;

import java.lang.annotation.Target;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.ejb.Local;
import javax.ejb.PrePassivate;
import javax.ejb.Remote;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ColumnResult;
import javax.persistence.ElementCollection;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;

import org.infodavid.common.ejb.IUserRemote;
import org.infodavid.common.model.IGenericPropertiesMap;
import org.infodavid.common.model.IGenericProperty;
import org.infodavid.common.model.IPerson;
import org.infodavid.common.model.IUser;
import org.infodavid.common.model.IUserGroup;
import org.infodavid.common.model.components.IEMail;
import org.infodavid.common.model.components.IEMail.EMailType;
import org.infodavid.common.model.components.IOwnerDetails.EVisibility;
import org.infodavid.common.model.listitems.IPersonListItem.EPersonTitle;
import org.infodavid.common.persistence.INamed;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;

/**
 * The Class UserBean.
 */
@Entity(name = "IUser", )
@Table(name = "USERS")

@Local(value = IUser.class)
@Remote(value = IUserRemote.class)
public class UserBean extends UserImpl {

  /** The Constant serialVersionUID. */
  private static final long serialVersionUID = 1660345027625851664L;

  /**
   * The Constructor.
   * @throws Exception the exception
   */
  protected UserBean() throws Exception {
    super();
  }

  /**
   * The Constructor.
   * @param keyArg the key arg
   * @throws Exception the exception
   */
  protected UserBean(final Long keyArg) throws Exception {
    super(keyArg);
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.UserImpl#init()
   */
  @Override
  @PrePassivate
  public void init() throws Exception {
    super.init();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.DataObjectImpl#getCreationDate()
   */
  @Override
  @Column(name = CREATION_DATE, nullable = false, updatable = false)
  @Type(type = "org.infodavid.persistence.impl.hibernate.usertype.DateTimeUserType")
  @Index(name = "IDX_USERS_CREATIONDATE")
  public DateTime getCreationDate() {
    return super.getCreationDate();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.DataObjectImpl#getModificationDate()
   */
  @Override
  @Version
  @Column(name = MODIFICATION_DATE, nullable = false)
  @Type(type = "org.infodavid.persistence.impl.hibernate.usertype.DateTimeUserType")
  public DateTime getModificationDate() {
    return super.getModificationDate();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.DataObjectImpl#getKey()
   */
  @Override
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = ID, nullable = false)
  public Long getKey() {
    return super.getKey();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.DataObjectImpl#isArchived()
   */
  @Override
  @Column(name = ARCHIVED, nullable = false)
  @Index(name = "IDX_USERS_ARCHIVED")
  public boolean isArchived() {
    return super.isArchived();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getName()
   */
  @Override
  @Column(name = NAME, length = 32, nullable = false, updatable = false)
  @Index(name = "IDX_USERS_NAME")
  public String getName() {
    return super.getName();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getComment()
   */
  @Override
  @Column(name = COMMENT, length = 1024, nullable = true)
  public String getComment() {
    return super.getComment();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getProperties()
   */
  @Override
  @Transient
  public IGenericPropertiesMap getProperties() {
    return super.getProperties();
  }

  /**
   * Gets the data.
   * @return the data
   */
  @ElementCollection(fetch = FetchType.LAZY, targetClass = GenericPropertyComponent.class)
  @CollectionTable(name = "USERS_PROPERTIES", joinColumns = @JoinColumn(name = ID, nullable = false), uniqueConstraints = @UniqueConstraint(name = "UC_USERS_PROPERTIES", columnNames = {
      INamed.NAME, ID
  }))
  @ForeignKey(name = "FK_USERS_PROPERTIES")
  protected List<IGenericProperty> getGenericData() {
    return super.getProperties().getData();
  }

  /**
   * Sets the generic data.
   * @param values the values
   */
  protected void setGenericData(final List<IGenericProperty> values) {
    super.getProperties().setData(values);
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getConnectionsCount()
   */
  @Override
  @Column(name = CONNECTIONS_COUNT, nullable = false)
  public long getConnectionsCount() {
    return super.getConnectionsCount();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getExpirationDate()
   */
  @Override
  @Column(name = EXPIRATION_DATE, nullable = true)
  @Type(type = "org.infodavid.persistence.impl.hibernate.usertype.DateTimeUserType")
  @Index(name = "IDX_USERS_EXPIRATIONDATE")
  public DateTime getExpirationDate() {
    return super.getExpirationDate();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getLastConnectionDate()
   */
  @Override
  @Column(name = LAST_CONNECTION_DATE, nullable = true)
  @Type(type = "org.infodavid.persistence.impl.hibernate.usertype.DateTimeUserType")
  public DateTime getLastConnectionDate() {
    return super.getLastConnectionDate();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getPseudonym()
   */
  @Override
  @Column(name = PSEUDONYM, length = 32, nullable = true)
  @Index(name = "IDX_USERS_PSEUDONYM")
  public String getPseudonym() {
    return super.getPseudonym();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.UserImpl#getTimeZone()
   */
  @Override
  @Column(name = TIME_ZONE, length = 48, nullable = true)
  @Type(type = "org.infodavid.persistence.impl.hibernate.usertype.DateTimeZoneUserType")
  public DateTimeZone getTimeZone() {
    return super.getTimeZone();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.UserImpl#getLocale()
   */
  @Override
  @Column(name = LOCALE, length = 3, nullable = true)
  @Type(type = "org.infodavid.persistence.impl.hibernate.usertype.LocaleLanguageUserType")
  public Locale getLocale() {
    return super.getLocale();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getLastIP()
   */
  @Override
  @Column(name = LAST_IP, length = 15, nullable = true)
  public String getLastIP() {
    return super.getLastIP();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getPassword()
   */
  @Override
  @Column(name = PASSWORD, length = 32, nullable = true)
  public String getPassword() {
    return super.getPassword();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#isLocked()
   */
  @Override
  @Column(name = LOCKED, nullable = false)
  public boolean isLocked() {
    return super.isLocked();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getPrimaryGroup()
   */
  @Override
  @Transient
  public IUserGroup getPrimaryGroup() {
    return super.getPrimaryGroup();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getPerson()
   */
  @Override
  @JoinColumn(name = IPerson.ID, referencedColumnName = IPerson.ID, nullable = true)
  @ManyToOne(cascade = CascadeType.ALL, targetEntity = PersonBean.class, fetch = FetchType.LAZY)
  @OnDelete(action = OnDeleteAction.CASCADE)
  @Cascade(value = {
      org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.SAVE_UPDATE
  })
  @ForeignKey(name = "FK_USERS_PERSON")
  public IPerson getPerson() {
    return super.getPerson();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getGroups()
   */
  @Override
  @ManyToMany(targetEntity = UserGroupBean.class, fetch = FetchType.LAZY)
  @JoinTable(name = "USERGROUPS_USERS", joinColumns = @JoinColumn(name = ID), inverseJoinColumns = @JoinColumn(name = IUserGroup.ID))
  @javax.persistence.MapKey(name = "name")
  @ForeignKey(name = "FK_USERS_USERGROUPS")
  public Map<String,IUserGroup> getGroups() {
    return super.getGroups();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.auth.model.impl.UserImpl#getEmail()
   */
  @Override
  @Embedded
  @AttributeOverrides({
      @AttributeOverride(name = "type", column = @Column(name = "emailType", nullable = false, length = 16)),
      @AttributeOverride(name = "value", column = @Column(name = "emailValue", nullable = false, length = 64))
  })
  @Target(value = EMailComponent.class)
  public IEMail getEmail() {
    return super.getEmail();
  }

  /**
   * Prepare the storage.
   */
  @PreUpdate
  @PrePersist
  protected void preStore() {
    final IEMail email = getEmail();

    if (email != null && email.getType() == null) {
      email.setType(EMailType.UNDEFINED);
    }
  }

  /**
   * Prepare the removal.
   */
  @PreRemove
  protected void preRemove() {
    setPrimaryGroup(null);
    getGroups().clear();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.UserImpl#getFirstName()
   */
  @Override
  @Column(name = FIRST_NAME, nullable = true, length = 64)
  @Index(name = "IDX_USERS_FIRSTNAME")
  public String getFirstName() {
    return super.getFirstName();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.UserImpl#getLastName()
   */
  @Override
  @Column(name = LAST_NAME, length = 96, nullable = true, unique = false)
  @Index(name = "IDX_USERS_LASTNAME")
  public String getLastName() {
    return super.getLastName();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.UserImpl#getTitle()
   */
  @Override
  @Column(name = TITLE, nullable = true, length = 32)
  @Enumerated(EnumType.STRING)
  public EPersonTitle getTitle() {
    return super.getTitle();
  }

  /*
   * (non-Javadoc)
   * @see org.infodavid.common.impl.model.UserImpl#getVisibility()
   */
  @Override
  @Column(name = VISIBILITY, nullable = true, length = 32)
  @Enumerated(EnumType.STRING)
  public EVisibility getVisibility() {
    return super.getVisibility();
  }
}
